package ar.edu.unq.wannaplay.helpers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joda.time.DateTime;
import org.joda.time.Interval;

import ar.edu.unq.wannaplay.calendareable.Licence;
import ar.edu.unq.wannaplay.calendareable.Project;
import ar.edu.unq.wannaplay.employee.Employee;
import ar.edu.unq.wannaplay.employee.Experience;
import ar.edu.unq.wannaplay.employee.Skill;

public class SkillsAndIntervalsStrategy extends RecommenderStrategy {

    private static final long serialVersionUID = 1L;

    @Override
    public Float getSatisfactionPercentage(final Employee employee, final Project project) {
        Float timePercentage = this.getTimePercentage(employee, project);
        Float skillsPercentage = this.getSkillsPercentage(employee, project);

        return (timePercentage + skillsPercentage) / 2;
    }

    private List<Interval> getTruncatedLicenceInterval(final Licence licence, final Project project) {
        Interval licenceInterval = licence.getInterval();
        Interval overlappedInterval = licenceInterval.overlap(project.getInterval());

        List<Interval> truncatedIntervals = new ArrayList<Interval>();
        if (overlappedInterval == null) {
            truncatedIntervals.add(licenceInterval);
        } else {
            DateTime licenceStart = licenceInterval.getStart();
            DateTime licenceEnd = licenceInterval.getEnd();

            DateTime overlappedIntervalStart = overlappedInterval.getStart();
            DateTime overlappedIntervalEnd = overlappedInterval.getEnd();

            if (!overlappedIntervalStart.equals(licenceStart)) {
                truncatedIntervals.add(new Interval(licenceStart, overlappedIntervalStart));
            }

            if (!overlappedIntervalEnd.equals(licenceEnd)) {
                truncatedIntervals.add(new Interval(overlappedIntervalEnd, licenceEnd));
            }

        }

        return truncatedIntervals;
    }

    private List<Interval> truncateLicenceSuperpositionswithProjects(final Employee employee, final Project project) {
        List<Licence> licences = employee.getRegister().getLicences();

        List<Interval> truncatedLicenceIntervals = new ArrayList<Interval>();
        List<Project> filteredProjects = new ArrayList<Project>();

        for (Project p : employee.getRegister().getProjects()) {
            if (project.getInterval().overlaps(p.getInterval())) {
                filteredProjects.add(p);
            }
        }

        for (Licence l : licences) {
            for (Project p : filteredProjects) {
                List<Interval> intervals = this.getTruncatedLicenceInterval(l, p);
                truncatedLicenceIntervals.addAll(intervals);
            }
        }

        return truncatedLicenceIntervals;
    }

    private float getTimePercentage(final Employee employee, final Project project) {
        Interval projectInterval = project.getInterval();
        int projectDays = projectInterval.getEnd().minusDays(projectInterval.getStart().getDayOfYear()).getDayOfYear();

        List<Interval> truncatedLicenceIntervals = this.truncateLicenceSuperpositionswithProjects(employee, project);

        int employeeProjectSuperpositionDays = 0;
        for (Project p : employee.getRegister().getProjects()) {
            Interval projectOverlap = p.getInterval().overlap(projectInterval);
            int overlappingDays = projectOverlap.getEnd().minusDays(projectOverlap.getStart().getDayOfYear())
                    .getDayOfYear();
            employeeProjectSuperpositionDays += overlappingDays;
        }

        for (Interval i : truncatedLicenceIntervals) {
            Interval licenceOverlap = i.overlap(projectInterval);
            int overlappingDays = licenceOverlap.getEnd().minusDays(licenceOverlap.getStart().getDayOfYear())
                    .getDayOfYear();
            employeeProjectSuperpositionDays += overlappingDays;
        }

        return (float) (projectDays - employeeProjectSuperpositionDays) * 100 / projectDays;
    }

    private Float getSkillsPercentage(final Employee employee, final Project project) {
        // Este método calcula el porcentaje de satisfacción que tienen las
        // skills de un empleado con los requerimientos de un proyecto.
        // Si la skill del empleado es igual a la del proyecto, esa skill
        // satisface al 100%. Si la skill del empleado es mayor que la del
        // proyecto, se le resta a la satisfacción 25% por cada
        // distanciamiento.
        // Si la skill del empleado es menor a la del proyecto se le resta a la
        // stisfacción 50% por cada distanciamiento.
        Map<Experience, Integer> expNumbers = new HashMap<Experience, Integer>();
        expNumbers.put(Experience.BASIC, 1);
        expNumbers.put(Experience.MEDIUM, 2);
        expNumbers.put(Experience.EXPERT, 3);

        int cantSkills = project.getRequirements().size();
        float percentageMatched = 0;
        for (Skill s : project.getRequirements()) {
            int numberNeeded = expNumbers.get(s.getExperience());
            int currentNumber = expNumbers.get(employee.getSkill(s.getKnowledge()).getExperience());
            float difference = numberNeeded - currentNumber;
            if (difference > 0) {
                percentageMatched += 100 - difference * 50;
            } else if (difference < 0) {
                percentageMatched += 100 - difference * -25;
            } else {
                percentageMatched += 100;
            }
        }

        return percentageMatched / cantSkills;
    }
}
